package zero.tech.wsnet.node;

import java.io.IOException;
import java.net.URI;

import javax.websocket.ClientEndpoint;
import javax.websocket.ContainerProvider;
import javax.websocket.DeploymentException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;

import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONObject;

import zero.tech.core.util.EngineLogUtil;
import zero.tech.core.work.WorkManager;
import zero.tech.wsnet.common.ResNodeProcess;
import zero.tech.wsnet.common.ResNodeProcessManager;
import zero.tech.wsnet.common.ResNodeQueueProcess;
import zero.tech.wsnet.data.Message;

@ClientEndpoint()
@Component
public class NodeClient {
	private String ip;
	private int port;
	private String playerId;
	private boolean autoReconnect;
	private boolean beKickOut;
	private Session session;
	private static WebSocketContainer container = null;
	private String url = "";
	

	public NodeClient(){
		
	}
	
	public NodeClient(String ip, int port, String playerId,
			boolean autoReconnect) {
		this.ip = ip;
		this.port = port;
		this.playerId = playerId;
		this.autoReconnect = autoReconnect;
		this.url="ws://"+ip+":"+port+"/discovery/engine/"+playerId;
	}

	public String getIp() {
		return ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public String getPlayerId() {
		return playerId;
	}

	public void setPlayerId(String playerId) {
		this.playerId = playerId;
	}

	public boolean isAutoReconnect() {
		return autoReconnect;
	}

	public void setAutoReconnect(boolean autoReconnect) {
		this.autoReconnect = autoReconnect;
	}

	public boolean isBeKickOut() {
		return beKickOut;
	}

	public void setBeKickOut(boolean beKickOut) {
		this.beKickOut = beKickOut;
	}

	public synchronized void sendMessage(Message out) {
		if (session.isOpen()) {
			try {
				session.getBasicRemote().sendText(JSONObject.toJSONString(out));
			} catch (IOException e) {
				EngineLogUtil.traceFull(e);
			}
		}
	}

	public void activeClose() {
		this.autoReconnect = false;
		if (session.isOpen()) {
			try {
				session.close();
			} catch (IOException e) {
				EngineLogUtil.traceFull(e);
			}
		} else {
			try {
				onClose(session);
			} catch (Exception e) {
				EngineLogUtil.traceFull(e);
			}
		}
	}

	public boolean connect() {
		if(container==null){
	        try {
	            container = ContainerProvider.getWebSocketContainer();
	        } catch (Exception ex) {
	        	ex.printStackTrace();
	        }
		}
        try {
            URI uri = URI.create(url);
            System.out.println("正在连接ip:" + ip + ",port:" + port + "----uri:"+url);
            session = container.connectToServer(this, uri);
        } catch (DeploymentException | IOException e) {
        	EngineLogUtil.traceFull(e);
        }
		
		if(session!=null&&session.isOpen()){
			return true;
		}
		return false;
	}


	private static boolean thread = false;

	public boolean isAlive() {
		return session.isOpen();
	}

	@OnClose
	public void onClose(Session session) {
		NodeCacheClient.remove(this);
		if (!this.beKickOut) {
			if (this.autoReconnect) {
				if (thread) {
					return;
				}
				thread = true;
				// 尝试自动重连
				new Thread(new Runnable() {

					@Override
					public void run() {
						boolean ok = false;
						while (!ok) {
							try {
								Thread.sleep(3000);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
							ok = connect();
							if (ok) {
								thread = false;
							}
						}
					}
				}).start();
			}
		}
	}

	@OnError
	public void onError(Session session, Throwable cause) {
		if (cause != null && cause.getMessage() != null) {
			if (cause.getMessage().startsWith("远程主机强迫关闭了一个现有的连接")
					|| cause.getMessage().startsWith("您的主机中的软件中止了一个已建立的连接")) {
				try {
					session.close();
				} catch (IOException e) {
					EngineLogUtil.traceFull(e);
				}
			}
		}
	}

	@OnMessage
	public void onMessage(Session session, String message) {
		Message in = JSONObject.parseObject(message, Message.class);
		String code = in.getHandle();
		Class<?> clz = ResNodeProcessManager.getManager().getProcess(code);
		if (clz == null) {
		} else {
			try {
				Object process = clz.newInstance();
				if (process instanceof ResNodeProcess) {
					ResNodeProcess net = (ResNodeProcess) process;
					net.init(in, this);
					net.run();
				} else if (process instanceof ResNodeQueueProcess) {
					ResNodeQueueProcess net = (ResNodeQueueProcess) process;
					net.init(in, this);
					WorkManager.getManager().submit(net);
				} else {
				}
			} catch (Exception e) {
				EngineLogUtil.traceFull(e);
			}
		}
	}

	@OnOpen
	public void onOpen(Session session) {
		this.session = session;
		NodeCacheClient.add(this);
	}
	
}
